package com.renfei.multimediatask.ui.task9.encodec;

import android.media.MediaCodec;
import android.media.MediaCodecInfo;
import android.media.MediaFormat;
import android.media.MediaMuxer;
import android.os.Build;
import android.util.Log;

import androidx.annotation.RequiresApi;

import com.renfei.multimediatask.ui.task9.VideoRecordAllProcess;
import com.renfei.multimediatask.ui.task9.prefect.VideoRecordAllProcessPrefect;
import com.renfei.multimediatask.util.YUVDataUtil;

import java.io.IOException;
import java.lang.ref.WeakReference;
import java.nio.ByteBuffer;

public class MediaVideoEncoder extends MediaEncoder implements Runnable {

    private static final boolean DEBUG = true;    // TODO set false on release
    private static final String TAG = "MediaVideoEncoder";


    @Override
    void prepare() {
        try {
            videoBufferInfo = new MediaCodec.BufferInfo();


            MediaFormat videoFormat = MediaFormat.createVideoFormat(MediaFormat.MIMETYPE_VIDEO_AVC, mWidth, mHeight);
            videoFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Flexible);
            videoFormat.setInteger(MediaFormat.KEY_FRAME_RATE, 25);
            videoFormat.setInteger(MediaFormat.KEY_BIT_RATE, mWidth * mHeight * 5);
            videoFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 1);

            videoEncodec = MediaCodec.createEncoderByType(MediaFormat.MIMETYPE_VIDEO_AVC);
            videoEncodec.configure(videoFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
            videoEncodec.start();
        } catch (IOException e) {
            e.printStackTrace();
        }

        Log.e(TAG, "MediaVideoEncoder: prepare  finish");
    }


    @Override
    void startRecording() {
        super.startRecording();
        new Thread(this, getClass().getSimpleName()).start();
        Log.e(TAG, "MediaVideoEncoder: startRecording");
    }

    @Override
    void stopRecording() {
        isEncode = false;
        super.stopRecording();

    }


    protected static final int TIMEOUT_USEC_INPUT = -1;
    protected static final int TIMEOUT_USEC = 10000;

    protected WeakReference<VideoRecordAllProcessPrefect> weakReference;

    private int mWidth;
    private int mHeight;
    private boolean isEncode = false;


    private MediaCodec videoEncodec;
    private MediaCodec.BufferInfo videoBufferInfo;

    private int videoTrackIndex = -1;

    public MediaVideoEncoder(MediaManager manager, WeakReference<VideoRecordAllProcessPrefect> weakReference, int mWidth, int mHeight) {
        super(manager);
        this.weakReference = weakReference;
        this.mWidth = mWidth;
        this.mHeight = mHeight;
    }

    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    public void run() {
        Log.e(TAG, "MediaVideoEncoder : thread  is runing ");
        isEncode = true;

        while (isEncode) {
            if (null != weakReference.get().queue && weakReference.get().queue.size() > 0) {

                byte[] inputData = weakReference.get().queue.poll();
                byte[] yuv420sp = new byte[mWidth * mHeight * 3 / 2];
                YUVDataUtil.NV21ToNV12(inputData, yuv420sp, mWidth, mHeight);
                inputData = yuv420sp;
                //输入缓冲区处理
                int inputBufferIndex = videoEncodec.dequeueInputBuffer(TIMEOUT_USEC_INPUT);
                if (inputBufferIndex >= 0) {
                    ByteBuffer byteBuffer = videoEncodec.getInputBuffer(inputBufferIndex);
                    byteBuffer.clear();
                    byteBuffer.put(inputData);
                    videoEncodec.queueInputBuffer(inputBufferIndex, 0, inputData.length, getInputPTSUs(), 0);
                } else {
                    Log.e(TAG, "输入缓冲区暂无可用: ");
                }
                //输出缓冲区处理
                int outputBufferIndex = videoEncodec.dequeueOutputBuffer(videoBufferInfo, TIMEOUT_USEC);
                if (outputBufferIndex == MediaCodec.INFO_TRY_AGAIN_LATER) {
                    Log.e(TAG, "输出缓冲区等待中: ");
                } else if (outputBufferIndex == MediaCodec.INFO_OUTPUT_BUFFERS_CHANGED) {
                    Log.e(TAG, "输出缓冲区已更改: ");
                } else if (outputBufferIndex == MediaCodec.INFO_OUTPUT_FORMAT_CHANGED) {
                    //输出格式更改时，重新给muxer设置
                    if (null != managerWeak.get().mMediaMuxer) {
                        videoTrackIndex = managerWeak.get().addTrack(videoEncodec.getOutputFormat());
                        Log.e(TAG, TAG + "    输出格式已更改: ");
                        managerWeak.get().start();
                    }
                } else if (outputBufferIndex < 0) {
                    Log.e(TAG, "出现其他类型的错误: ");
                } else {

                    while (outputBufferIndex >= 0) {
                        ByteBuffer outputBuffer = videoEncodec.getOutputBuffer(outputBufferIndex);

                        if (managerWeak.get().isMuxerStarted) {
                            outputBuffer.position(videoBufferInfo.offset);
                            outputBuffer.limit(videoBufferInfo.offset + videoBufferInfo.size);

                            videoBufferInfo.presentationTimeUs = getPTSUs();

                            Log.e(TAG,"videoBufferInfo.presentationTimeUs   "+videoBufferInfo.presentationTimeUs     );
                            Log.e(TAG,"videoBufferInfo.size                 "+videoBufferInfo.size                   );
                            Log.e(TAG,"videoBufferInfo.flags                "+videoBufferInfo.flags                  );
                            Log.e(TAG,"videoBufferInfo.offset               "+videoBufferInfo.offset                 );


                            //写入数据
                            managerWeak.get().writeSampleData(videoTrackIndex, outputBuffer, videoBufferInfo);

                            prevOutputPTSUs = videoBufferInfo.presentationTimeUs;


                            Log.e(TAG, "写入数据到文件。。。: " + videoBufferInfo.size);
                        } else {
                            Log.e(TAG, "isMediaMuxerStart:  mediaMuxer is not start");
                        }
                        videoEncodec.releaseOutputBuffer(outputBufferIndex, false);
                        outputBufferIndex = videoEncodec.dequeueOutputBuffer(videoBufferInfo, TIMEOUT_USEC);

                    }
                }
            }
        }

        videoEncodec.stop();
        videoEncodec.release();
        videoEncodec = null;

        isEncode = false;
        Log.e(TAG, "录制已经完成: ");
    }

    /**
     * previous presentationTimeUs for writing
     */
    private long prevOutputPTSUs = 0;

    /**
     * get next encoding presentationTimeUs
     *
     * @return
     */
    protected long getPTSUs() {
        long result = System.nanoTime() / 1000L;
        // presentationTimeUs should be monotonic
        // otherwise muxer fail to write
        if (result < prevOutputPTSUs)
            result = (prevOutputPTSUs - result) + result;
        return result;
    }


    private long previnputPTSUs = 0;

    /**
     * get next encoding presentationTimeUs
     *
     * @return
     */
    protected long getInputPTSUs() {
        long result = System.nanoTime() / 1000L;
        // presentationTimeUs should be monotonic
        // otherwise muxer fail to write
        if (result < previnputPTSUs)
            result = (previnputPTSUs - result) + result;
        return result;
    }
}
